package com.datasure.demo.webSocket.server;

import java.util.Date;

import com.datasure.demo.webSocket.config.ChannelManage;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.*;
import io.netty.handler.codec.http.websocketx.*;
import io.netty.util.CharsetUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class WebsocketChannelHandler extends SimpleChannelInboundHandler<Object> {
    @Autowired
    private ChannelManage channelManage;

    private WebSocketServerHandshaker handshaker;
    private static final String web_socket_url="ws://127.0.0.1:8888/websocket";
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {
        if(msg instanceof FullHttpMessage) {
            handleHttpRequest(ctx,(FullHttpRequest)msg);
        }else if(msg instanceof WebSocketFrame) {
            handleWebSocketFrame(ctx,(WebSocketFrame)msg);
        }
    }

    private void handleWebSocketFrame(ChannelHandlerContext ctx, WebSocketFrame frame) {
        if(frame instanceof CloseWebSocketFrame) {
            handshaker.close(ctx.channel(), (CloseWebSocketFrame)frame.retain());
        }

        if(frame instanceof PingWebSocketFrame) {
            ctx.channel().write(new PongWebSocketFrame(frame.content().retain()));
            return;
        }
        if(!(frame instanceof TextWebSocketFrame)) {
            System.out.println("目前我们不支持二进制消息");
            new RuntimeException("["+this.getClass().getName()+"]不支持消息");
        }
        String request = ((TextWebSocketFrame)frame).text();
        System.out.println("服务端接收到消息==>"+request);
        TextWebSocketFrame res = new TextWebSocketFrame(new Date().toString()+"-"+ctx.channel().id()+"===>"+request);
        // 根据channel直接输出
//        NettyConfig.group.writeAndFlush(res);
        ctx.channel().writeAndFlush(res);
    }


    private void handleHttpRequest(ChannelHandlerContext ctx, FullHttpRequest request) {
        String uri = request.uri();
        channelManage.add(ctx.channel(), uri);

        if(!request.decoderResult().isSuccess()||!"websocket".equals(request.headers().get("Upgrade"))) {
            sendHttpResponse(ctx, request, new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.BAD_REQUEST));
            return;
        }
        WebSocketServerHandshakerFactory factory = new WebSocketServerHandshakerFactory(web_socket_url, null, false);
        handshaker = factory.newHandshaker(request);
        if(handshaker==null) {
            WebSocketServerHandshakerFactory.sendUnsupportedVersionResponse(ctx.channel());
        }else {
            handshaker.handshake(ctx.channel(), request);
        }

    }

    private void sendHttpResponse(ChannelHandlerContext ctx,FullHttpRequest request,DefaultFullHttpResponse response) {
        if(response.status().code()!=200) {
            ByteBuf buf = Unpooled.copiedBuffer(response.status().toString(),CharsetUtil.UTF_8);
            response.content().writeBytes(buf);
            buf.release();
        }
        ChannelFuture future = ctx.channel().writeAndFlush(response);
        if(response.status().code()!=200) {
            future.addListener(ChannelFutureListener.CLOSE);
        }
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("客户端与服务端建立连接");
//        NettyConfig.group.add(ctx.channel());
        TextWebSocketFrame res = new TextWebSocketFrame(new Date().toString()+"-"+ctx.channel().id()+"===>连接成功");
//        ctx.channel().writeAndFlush(res);
//        NettyConfig.group.writeAndFlush(res);
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("客户端与服务端断开连接");
//        NettyConfig.group.remove(ctx.channel());

        channelManage.remove(ctx.channel());
    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        ctx.flush();
    }

}